Remediation scripts/Create Azure RBAC Role based on CIEM access report/Create Azure RBAC Role based on CIEM access report.ps1 (496 lines of code) (raw):
<#
.NOTES
===========================================================================
Created with: SAPIEN Technologies, Inc., PowerShell Studio 2022 v5.8.213
Created on: 05-01-2024 18:41
Created by: Michael Morten Sonne
Organization: Sonne´s Cloud
Blog: https://blog.sonnes.cloud
GitHub: https://github.com/michaelmsonne
Filename: 'Create Azure RBAC Role based on CIEM access report - used access scopes.ps1'
===========================================================================
.DESCRIPTION
PowerShell script to create custom Azure RBAC role based on output from CIEM access report in CSV format.
The script will prompt for the name and description of the custom role to be created.
The script will prompt for confirmation before creating the custom role.
The script will prompt for the path to the CSV file containing the CIEM access report.
The script will prompt for the resource group or subscription to scope the custom role to.
The script will prompt for confirmation before creating the custom role.
.REQUREMENT
- Azure subscription
- Microsoft Azure PowerShell Az module (at least 'Az.Accounts' and 'Az.Resources')
- Right permissions to create custom Azure RBAC roles (Owner or User Access Administrator, or custom role with Microsoft.Authorization/roleDefinitions/write permission)
.CHANGELOG
05-01-2024 - Michael Morten Sonne - Initial release
06-02-2024 - Michael Morten Sonne - Some small changes to the script and add GridView for large datasets if needed (some work done too before here but not documented)
07-03-2024 - Michael Morten Sonne - Some small changes and typos fixed and added try/catch to the script for install of Az modules
.EXAMPLE
Create a custom Azure RBAC role based on CIEM access report in CSV format and display the unique access scopes and count in the console
PS C:\> .\'Create Azure RBAC Role based on CIEM access report - used access scopes.ps1' -CsvFilePath "C:\Users\MichaelMortenSonne\Actions.csv"
Create a custom Azure RBAC role based on CIEM access report in CSV format and display the unique access scopes and count in a grid view (usefull for large datasets)
PS C:\> .\'Create Azure RBAC Role based on CIEM access report - used access scopes.ps1' -GridView -CsvFilePath "C:\Users\MichaelMortenSonne\Actions.csv"
#>
param (
[Parameter(Mandatory=$true)]
[string]$CsvFilePath,
[Parameter(Mandatory=$false)]
[switch]$GridView = $null,
[Parameter(Mandatory=$false)]
[string]$ResourceGroup
)
function Invoke-Script
{
<#
.SYNOPSIS
Starts the script and checks for required modules and login to Azure
.EXAMPLE
Invoke-Script -Banner
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)][switch]$Checks = $null,
[Parameter(Mandatory=$false)][switch]$CheckLogin = $null,
[Parameter(Mandatory=$false)][switch]$Banner = $null)
# Check if the user is logged in to Azure
If ($CheckLogin)
{
#Login Check
$AZSUser = Get-AzContext
if(!$AZSUser)
{
# Prompt the user to login to Azure
Write-Host "Please login with Connect-AzAccount - see popup promt" -ForegroundColor Yellow
# Try to connect to Azure via Az module
try{
# Connect to Azure via Az module
Connect-AzAccount
}catch{
# Failed to connect to Azure
Write-Host "Failed to call Connect-AzAccount: $($_.Exception.Message)" -ForegroundColor Red
return $False
}
}
}
# Check if the required modules are installed and supported PowerShell version
If($Checks)
{
# Set ErrorActionPreference to Stop
$ErrorActionPreference = "Stop"
# Check for supported PowerShell version
$Version = $PSVersionTable.PSVersion.Major
If ($Version -lt 5)
{
# PowerShell version is not supported - 5.1 or later is required
Write-Host "Az requires at least PowerShell 5.1 - Exiting..." -ForegroundColor Red
Exit
}
# Check Modules Az.Accounts and Az.Resources is installed
$Modules = Get-InstalledModule
if ($Modules.Name -notcontains 'Az.Accounts' -and $Modules.Name -notcontains 'Az.Resources')
{
# Az PowerShell Modules not installed - prompt the user to install the modules
Write-host "Install Az PowerShell Modules?" -ForegroundColor Yellow
$Readhost = Read-Host " ( y / n ) "
# Install Az PowerShell Modules if the user confirms
if ($ReadHost -eq 'y' -or $Readhost -eq 'yes')
{
try {
# Install Az PowerShell Modules
Install-Module -Name Az -AllowClobber -Scope CurrentUser
# Get installed modules
$Modules = Get-InstalledModule
# Check if the Az PowerShell Modules are installed
if ($Modules.Name -contains 'Az.Accounts' -and $Modules.Name -contains 'Az.Resources')
{
Write-Host "Successfully installed Az modules" -ForegroundColor Green
}
}
catch {
# Failed to install Az PowerShell Modules show error message
Write-Host "Failed to install Az modules: $($_.Exception.Message)" -ForegroundColor Red
}
}
# Exit if the user does not confirm to install the Az PowerShell Modules
if ($ReadHost -eq 'n' -or $Readhost -eq 'no')
{
# User did not confirm to install the Az PowerShell Modules - exit
Write-Host "Az PowerShell not installed, This script cannot operate without this modules, exiting..." -ForegroundColor Red
Exit
}
}
else
{
# Az PowerShell Modules needed is installed!
Write-Host "Az PowerShell Modules needed is installed - good!`n" -ForegroundColor Green
}
#Login Check
$AZSStartUser = Get-AzContext
if(!$AZSStartUser)
{
Write-Host "Remember to login with Connect-AzAccount if you will create a custom role in Azure RBAC!`n" -ForegroundColor Yellow
}
}
# Banner
if($Banner)
{
Write-Host "Please set your default subscription with " -ForegroundColor yellow -NoNewline
Write-Host "Set-AzContext " -ForegroundColor Magenta -NoNewline
Write-Host "if you have multiple subscriptions. Functions will fail if you not set one. Use " -ForegroundColor yellow -NoNewline
Write-Host "Get-AzSubscription" -ForegroundColor Magenta -NoNewline
Write-Host " to get a list of your subscriptions.`n" -ForegroundColor Yellow
}
# If not logged in to Azure
if(!$Checks -and !$CheckLogin -and !$Banner)
{
Write-Host "Please login with Connect-AzAccount" -ForegroundColor Red
}
}
function CheckConnectionToAzure {
# Check if connected to Azure
$azContext = Get-AzContext -ErrorAction SilentlyContinue
# If connected to Azure
if ($azContext) {
# If connected to Azure - prompt for logout if connected to Azure before exiting the script
Write-Host "You are currently connected to Azure." -ForegroundColor Yellow
$confirmLogout = Read-Host "Do you want to log out from Azure? (Type 'yes' to confirm)"
# Check user confirmation
if ($confirmLogout.ToLower() -eq 'yes' -or $confirmLogout.ToLower() -eq 'y') {
try {
# Log out from Azure
Disconnect-AzAccount
# Show logged out from Azure
Write-Host "Logged out from Azure." -ForegroundColor Green
} catch {
# Show error message if failed to log out from Azure
Write-Host "Not logged out from Azure." -ForegroundColor Red
Write-Host "An error occurred while logging out from Azure: $($_.Exception.Message)" -ForegroundColor Red
}
} else {
# Show message if the user selected to not logout from Azure - Session is active in your console
Write-Host "You selected to not logout from Azure - Session is active in your console." -ForegroundColor Red
}
} else {
# Show message if not connected to Azure - do nothing
Write-Host "You are not currently connected to Azure." -ForegroundColor Green
}
}
# Start the script and check for required modules
Invoke-Script -Checks
# Read the CSV file
$data = Import-Csv -Path $CsvFilePath
# Extracting just the filename from the $CsvFilePath
$csvFileName = (Get-Item $CsvFilePath).Name
# Filter out rows where 'used' is not empty
$usedData = $data | Where-Object { $_.used -ne "" } | Select-Object -ExpandProperty used
if ($usedData.Count -gt 0) {
# Filter out empty values and split the access scopes
$accessScopes = $usedData -split ',' | Where-Object { $_ -ne "" } | ForEach-Object { $_.Trim() }
# Count the unique access scopes
$uniqueAccessScopes = $accessScopes | Sort-Object -Unique
$numberOfAccessScopes = $uniqueAccessScopes.Count
# Display the unique access scopes and count in a grid view or in the console
if ($GridView){
# Create a custom object with renamed columns
$customuniqueAccessScopes = $uniqueAccessScopes | ForEach-Object {
[PSCustomObject]@{
'Custom Azure RBAC Scopes to use in new role' = $_ # Custom column name
}
}
# Display the unique access scopes and count in a grid view
$customuniqueAccessScopes | Out-GridView -Title "$numberOfAccessScopes Unique access scopes discovered within the exported file '$csvFileName' and are intended for use in a new custom Azure RBAC role" -PassThru
}
else {
# Display the unique access scopes and count
Write-Host "Unique access scopes were discovered within the exported file '$csvFileName' and are intended for use in a new custom Azure RBAC role:`n"
# Display the unique access scopes
$uniqueAccessScopes
# Display the number of unique access scopes
Write-Host "`nNumber of Unique Access Scopes: " -NoNewline
Write-Host $numberOfAccessScopes `n -ForegroundColor Yellow
}
} else {
# No 'used' data found in the CSV file - abort
Write-Host "`nNo 'used' data found. Aborting..."
exit
}
# Prompt user confirmation
$confirmation = Read-Host "Do you want to proceed and create a custom role in Azure RBAC? (Type 'yes' to confirm)"
# Check user confirmation
if ($confirmation.ToLower() -eq "yes" -or $confirmation.ToLower() -eq "y") {
# Create a new role definition - Ensure you have the necessary permissions and provide the required role definition details
# Start the script and check for login
Invoke-Script -CheckLogin
# Get the current subscription data
$subscriptionID = (Get-AzContext).Subscription.Id
$subscriptionName = (Get-AzContext).Subscription.Name
# Show the current Azure Subscription
Write-Host "Current Azure Subscription for your session:`nID: $subscriptionID`nName: $subscriptionName`n"
# Start the script and check for used subscription
Invoke-Script -Banner
# Prompt user confirmation
$confirmation = Read-Host "Is the Azure Subscription above the correct one you will work with? (Type 'yes' to confirm)"
# Check user confirmation
if ($confirmation.ToLower() -eq "yes" -or $confirmation.ToLower() -eq "y")
{
# Ask for role name
$var_RoleName = Read-Host "Enter the name of the custom role to be created"
# Ask for role description
$var_Description = Read-Host "Enter the description of the custom role to be created"
# Create a new role definition - copy the role definition and update the name, description, and access scopes to match the exported data from CIEM
$role = Get-AzRoleDefinition -Name "Reader"
# Blank the ID to prevent overwriting
$role.Id = $null
$role.Name = $var_RoleName
$role.Description = $var_Description
# Initialize actions collections
$role.actions = New-Object System.Collections.Generic.List[string]
# Add access scopes to the role definition
foreach ($scope in $accessScopes) {
$role.Actions.Add($scope)
}
# Clear the assignable scopes and add the subscription ID as the assignable scope
$role.AssignableScopes.Clear()
if (-not $ResourceGroup) {
$role.AssignableScopes.Add("/subscriptions/$subscriptionID")
} else {
$role.AssignableScopes.Add("/subscriptions/$subscriptionID/resourceGroups/$ResourceGroup/")
}
# Display the role definition is about to be created
Write-Host "Creating custom role in Azure RBAC..." -ForegroundColor Yellow
# Create the custom role in Azure RBAC
try {
New-AzRoleDefinition -Role $role
Write-Host "Azure role definition created successfully.`n" -ForegroundColor Green
} catch {
Write-Host "An error occurred while creating the Azure role definition: $($_.Exception.Message)" -ForegroundColor Red
}
}
else {
Write-Host "Operation canceled. Set the current Azure Subscription to use."
}
} else {
Write-Host "Operation canceled. Custom role creation aborted!"
}
# Check if connected to Azure and prompt for logout if connected to Azure before exiting the script
CheckConnectionToAzure
# SIG # Begin signature block
# MIIo7QYJKoZIhvcNAQcCoIIo3jCCKNoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAMKZ+su5SIxycd
# pLpFwEwEgfIgiOp0ucQzt3opL534d6CCEd8wggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZKMIIEsqADAgECAhAR4aCGZIeugmCCjSjwUXrGMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjMwMjE5MDAwMDAwWhcNMjYwNTE4MjM1OTU5WjBhMQswCQYDVQQGEwJESzEU
# MBIGA1UECAwLSG92ZWRzdGFkZW4xHTAbBgNVBAoMFE1pY2hhZWwgTW9ydGVuIFNv
# bm5lMR0wGwYDVQQDDBRNaWNoYWVsIE1vcnRlbiBTb25uZTCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBALVGIWG57aPiOruK3bg3tlPMHol1pfnEQiCkYom7
# hFXLVxhGve4OcQmx9xtKy7QIHmbHdH3Vc4J4foS0/bv4cnzYRd0g2qcTjo0Q+b5J
# RUSZQ0yUbLyHJf1TkCJOODWORJlsi/xppcQdAbU7QX2KFE4NkQzNUIOTSlKctx99
# ZqFevKIvwhkmIoB+WWnl/qS4ipFMO/d4m7o8IIgi49LPq3tVxZs0aJ6N02X5Xp2F
# oG2fZynudHIf9waYFtYXA3B8msQwaREpQY880Kki/275pSC+T8+mbnbwrKXOZ8Gj
# W2vvEJZe5ySIrA27omMsBnmoZYkiNMmMGYWQiZ5E75ZIiZ4UqWpuahoGpBLoZNX+
# TjKFFuqmo8EqfYdCpLiYgw95q3gHONu6TwTg01WwaeZFtlhx8qSgD8x7L/SRn4qn
# x//ucBg1Q0f3Al6lz++z8t4ty6CxF/Wr9ZKOoYhHft6SAE7Td9VGdWJLkp6cY1qf
# rq+QA+xR7rjFi7dagxvP1RzZqeh5glAQ74g3/lZJdgDTv/yB/zjxj6dHjzwii501
# VW4ecSX9RQpwWbleDDriDbVNJxwz37mBcSQykGXVfVV8AcdXn1zvEDkdshtLUGAL
# 6q61CugAE4LoOWohBEtk7dV2X0rvEY3Wce47ATLY14VM5gQCEsRxkEqt1HwdK4R+
# v/LtAgMBAAGjggGJMIIBhTAfBgNVHSMEGDAWgBQPKssghyi47G9IritUpimqF6TN
# DDAdBgNVHQ4EFgQUdfN+UjqPPYYWLqh4zXaTNj8AfJswDgYDVR0PAQH/BAQDAgeA
# MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYDVR0gBEMwQTA1
# BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNv
# bS9DUFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2Vj
# dGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsG
# AQUFBwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9T
# ZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0
# dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQBF8qhaDXok
# 5R784NqfjMsNfS97H+ItE+Sxm/QMcIhTiiIBhIYd/lLfdTwpz5aqTl5M4+FDBDeN
# m0mjY8k2Cdg+DOf4JfvZAv4tQVybhEd42E5NTfG5sWN6ruMjBLpSsjwVzvonmeUL
# SwnXY+AtVSag0MU/UnyFOTS69gTjOq3EC+H/OJa/DfI8T/sDICzTy55c5aCDHRXb
# 6Dsr+Hm7PiGCQ6c0AhYOt/etXK1+YjQo9T+FcIF0Ze34CKirIRa1FFe26gNjHdpr
# MA62TOXQJrK+x9DtVY8QCb+IUZNYj6lNiXno3t69JN6FvIU2EtPrKs8SBV2uDZQM
# ecNJ+3w77/EHod82uB73vGiOvX8Q2CkdMunz+VfXyY4Oh10AEnCqzl0UV2HHH66H
# sa8Zti+kXWH9HTUkDJCd2VHdDEOJ0o2kA1/SfETMPAO/yeFz1xXy6CIJ50dkfzuY
# gf9SsIAod1Dx9THs2qkXIwyf5lTJBvPHLRqxs/k+Mn70AUiyj50/JYMxghZkMIIW
# YAIBATBoMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQx
# KzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYCEBHh
# oIZkh66CYIKNKPBResYwDQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIw
# ADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYK
# KwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgIzbpLjtzs1kgX1MFCg9dIMrqDsZD
# HeCZXQFn5NMQOUEwDQYJKoZIhvcNAQEBBQAEggIAfpKZkrQdGIH8vY3huyOC2Ejl
# zav2xeJBml518ha6lgC8bqjAB3evtZcTDpUvfE+On64BoezADAfQ2UbvtqN70+0f
# 9xt7NnMgu21QWSa+3atqgm8X+Kg+6PVaN5v48ex5vUl/NA5c/LMMICdgrebKn+A0
# APpy/J93S1XheUEX0gaFFHOyYzcytzFz3vTPXTUSULFgYyWXpXJGfpk5+pyDjpkb
# uavweXt12SS6UIHgb0GsYSFVLPg6QtdCMd8NfiEBFqmpgUPvEONCzjLZxuE7gcJD
# Sy+u4BHrMlRHf133cueQE23qFP1dtbbSMzFkKOMQXi53CJ/B1gun6dwv8yk+wvKy
# 0f6I178CgmUER7j2d/62RpS6Lj0MotAzq0yKaJOeW0qNMYBM3K2ThuoVneJ1BYLW
# PQV6/80OgYl0GQs0MLzEFsziMGjeTu8tko3GyHLPPeUILOqlOa75iAhchmD1Lh+D
# 5T0dSQNaKHsic9B5Akvh1wxgwNFI2u7i0ColaiFm1o87F/7NBoMpgDVY04+rmEvi
# Qr3pDgnHLHvvtgxpCn9xffT7vtQpgax/WUGITibSMJpnw6nQAxkQyKMIKT/QZUh8
# 9mAYSn7GFTXdj7XpFNyffWsKSuRF4Y84hqpO13fubrhbWdtWgNRMF81hjeMlsHdR
# DXSc6Tcvi+8IACKBsLuhghNPMIITSwYKKwYBBAGCNwMDATGCEzswghM3BgkqhkiG
# 9w0BBwKgghMoMIITJAIBAzEPMA0GCWCGSAFlAwQCAgUAMIHwBgsqhkiG9w0BCRAB
# BKCB4ASB3TCB2gIBAQYKKwYBBAGyMQIBATAxMA0GCWCGSAFlAwQCAQUABCBlQ8i8
# MYy10uxTy0aHh3zM0EAerJn5nufbcmwiYuF6WQIVAJRGXC+QofEsS8vXQV7ozdAs
# 8W8jGA8yMDI0MDMwOTE0NDAxN1qgbqRsMGoxCzAJBgNVBAYTAkdCMRMwEQYDVQQI
# EwpNYW5jaGVzdGVyMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMM
# I1NlY3RpZ28gUlNBIFRpbWUgU3RhbXBpbmcgU2lnbmVyICM0oIIN6TCCBvUwggTd
# oAMCAQICEDlMJeF8oG0nqGXiO9kdItQwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UE
# BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2Fs
# Zm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSUwIwYDVQQDExxTZWN0aWdv
# IFJTQSBUaW1lIFN0YW1waW5nIENBMB4XDTIzMDUwMzAwMDAwMFoXDTM0MDgwMjIz
# NTk1OVowajELMAkGA1UEBhMCR0IxEzARBgNVBAgTCk1hbmNoZXN0ZXIxGDAWBgNV
# BAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAwwjU2VjdGlnbyBSU0EgVGltZSBT
# dGFtcGluZyBTaWduZXIgIzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQCkkyhSS88nh3akKRyZOMDnDtTRHOxoywFk5IrNd7BxZYK8n/yLu7uVmPslEY5a
# iAlmERRYsroiW+b2MvFdLcB6og7g4FZk7aHlgSByIGRBbMfDCPrzfV3vIZrCftcs
# w7oRmB780yAIQrNfv3+IWDKrMLPYjHqWShkTXKz856vpHBYusLA4lUrPhVCrZwMl
# obs46Q9vqVqakSgTNbkf8z3hJMhrsZnoDe+7TeU9jFQDkdD8Lc9VMzh6CRwH0SLg
# Y4anvv3Sg3MSFJuaTAlGvTS84UtQe3LgW/0Zux88ahl7brstRCq+PEzMrIoEk8ZX
# hqBzNiuBl/obm36Ih9hSeYn+bnc317tQn/oYJU8T8l58qbEgWimro0KHd+D0TAJI
# 3VilU6ajoO0ZlmUVKcXtMzAl5paDgZr2YGaQWAeAzUJ1rPu0kdDF3QFAaraoEO72
# jXq3nnWv06VLGKEMn1ewXiVHkXTNdRLRnG/kXg2b7HUm7v7T9ZIvUoXo2kRRKqLM
# AMqHZkOjGwDvorWWnWKtJwvyG0rJw5RCN4gghKiHrsO6I3J7+FTv+GsnsIX1p0OF
# 2Cs5dNtadwLRpPr1zZw9zB+uUdB7bNgdLRFCU3F0wuU1qi1SEtklz/DT0JFDEtcy
# fZhs43dByP8fJFTvbq3GPlV78VyHOmTxYEsFT++5L+wJEwIDAQABo4IBgjCCAX4w
# HwYDVR0jBBgwFoAUGqH4YRkgD8NBd0UojtE1XwYSBFUwHQYDVR0OBBYEFAMPMciR
# KpO9Y/PRXU2kNA/SlQEYMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYG
# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMEoGA1UdIARDMEEwNQYMKwYBBAGyMQECAQMI
# MCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAEE
# AjBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3Rp
# Z29SU0FUaW1lU3RhbXBpbmdDQS5jcmwwdAYIKwYBBQUHAQEEaDBmMD8GCCsGAQUF
# BzAChjNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29SU0FUaW1lU3RhbXBp
# bmdDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0G
# CSqGSIb3DQEBDAUAA4ICAQBMm2VY+uB5z+8VwzJt3jOR63dY4uu9y0o8dd5+lG3D
# IscEld9laWETDPYMnvWJIF7Bh8cDJMrHpfAm3/j4MWUN4OttUVemjIRSCEYcKsLe
# 8tqKRfO+9/YuxH7t+O1ov3pWSOlh5Zo5d7y+upFkiHX/XYUWNCfSKcv/7S3a/76T
# DOxtog3Mw/FuvSGRGiMAUq2X1GJ4KoR5qNc9rCGPcMMkeTqX8Q2jo1tT2KsAulj7
# NYBPXyhxbBlewoNykK7gxtjymfvqtJJlfAd8NUQdrVgYa2L73mzECqls0yFGcNwv
# jXVMI8JB0HqWO8NL3c2SJnR2XDegmiSeTl9O048P5RNPWURlS0Nkz0j4Z2e5Tb/M
# DbE6MNChPUitemXk7N/gAfCzKko5rMGk+al9NdAyQKCxGSoYIbLIfQVxGksnNqrg
# mByDdefHfkuEQ81D+5CXdioSrEDBcFuZCkD6gG2UYXvIbrnIZ2ckXFCNASDeB/cB
# 1PguEc2dg+X4yiUcRD0n5bCGRyoLG4R2fXtoT4239xO07aAt7nMP2RC6nZksfNd1
# H48QxJTmfiTllUqIjCfWhWYd+a5kdpHoSP7IVQrtKcMf3jimwBT7Mj34qYNiNsjD
# vgCHHKv6SkIciQPc9Vx8cNldeE7un14g5glqfCsIo0j1FfwET9/NIRx65fWOGtS5
# QDCCBuwwggTUoAMCAQICEDAPb6zdZph0fKlGNqd4LbkwDQYJKoZIhvcNAQEMBQAw
# gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK
# ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD
# VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE5
# MDUwMjAwMDAwMFoXDTM4MDExODIzNTk1OVowfTELMAkGA1UEBhMCR0IxGzAZBgNV
# BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
# ChMPU2VjdGlnbyBMaW1pdGVkMSUwIwYDVQQDExxTZWN0aWdvIFJTQSBUaW1lIFN0
# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyBsBr9ks
# foiZfQGYPyCQvZyAIVSTuc+gPlPvs1rAdtYaBKXOR4O168TMSTTL80VlufmnZBYm
# CfvVMlJ5LsljwhObtoY/AQWSZm8hq9VxEHmH9EYqzcRaydvXXUlNclYP3MnjU5g6
# Kh78zlhJ07/zObu5pCNCrNAVw3+eolzXOPEWsnDTo8Tfs8VyrC4Kd/wNlFK3/B+V
# cyQ9ASi8Dw1Ps5EBjm6dJ3VV0Rc7NCF7lwGUr3+Az9ERCleEyX9W4L1GnIK+lJ2/
# tCCwYH64TfUNP9vQ6oWMilZx0S2UTMiMPNMUopy9Jv/TUyDHYGmbWApU9AXn/TGs
# +ciFF8e4KRmkKS9G493bkV+fPzY+DjBnK0a3Na+WvtpMYMyou58NFNQYxDCYdIIh
# z2JWtSFzEh79qsoIWId3pBXrGVX/0DlULSbuRRo6b83XhPDX8CjFT2SDAtT74t7x
# vAIo9G3aJ4oG0paH3uhrDvBbfel2aZMgHEqXLHcZK5OVmJyXnuuOwXhWxkQl3wYS
# mgYtnwNe/YOiU2fKsfqNoWTJiJJZy6hGwMnypv99V9sSdvqKQSTUG/xypRSi1K1D
# HKRJi0E5FAMeKfobpSKupcNNgtCN2mu32/cYQFdz8HGj+0p9RTbB942C+rnJDVOA
# ffq2OVgy728YUInXT50zvRq1naHelUF6p4MCAwEAAaOCAVowggFWMB8GA1UdIwQY
# MBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBQaofhhGSAPw0F3RSiO
# 0TVfBhIEVTAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYDVR0fBEkwRzBF
# oEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUNlcnRp
# ZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/BggrBgEFBQcw
# AoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUFkZFRydXN0
# Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0G
# CSqGSIb3DQEBDAUAA4ICAQBtVIGlM10W4bVTgZF13wN6MgstJYQRsrDbKn0qBfW8
# Oyf0WqC5SVmQKWxhy7VQ2+J9+Z8A70DDrdPi5Fb5WEHP8ULlEH3/sHQfj8ZcCfkz
# XuqgHCZYXPO0EQ/V1cPivNVYeL9IduFEZ22PsEMQD43k+ThivxMBxYWjTMXMslMw
# laTW9JZWCLjNXH8Blr5yUmo7Qjd8Fng5k5OUm7Hcsm1BbWfNyW+QPX9FcsEbI9bC
# VYRm5LPFZgb289ZLXq2jK0KKIZL+qG9aJXBigXNjXqC72NzXStM9r4MGOBIdJIct
# 5PwC1j53BLwENrXnd8ucLo0jGLmjwkcd8F3WoXNXBWiap8k3ZR2+6rzYQoNDBaWL
# pgn/0aGUpk6qPQn1BWy30mRa2Coiwkud8TleTN5IPZs0lpoJX47997FSkc4/ifYc
# obWpdR9xv1tDXWU9UIFuq/DQ0/yysx+2mZYm9Dx5i1xkzM3uJ5rloMAMcofBbk1a
# 0x7q8ETmMm8c6xdOlMN4ZSA7D0GqH+mhQZ3+sbigZSo04N6o+TzmwTC7wKBjLPxc
# FgCo0MR/6hGdHgbGpm0yXbQ4CStJB6r97DDa8acvz7f9+tCjhNknnvsBZne5VhDh
# IG7GrrH5trrINV0zdo7xfCAMKneutaIChrop7rRaALGMq+P5CslUXdS5anSevUiu
# mDGCBCwwggQoAgEBMIGRMH0xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVy
# IE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28g
# TGltaXRlZDElMCMGA1UEAxMcU2VjdGlnbyBSU0EgVGltZSBTdGFtcGluZyBDQQIQ
# OUwl4XygbSeoZeI72R0i1DANBglghkgBZQMEAgIFAKCCAWswGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNDAzMDkxNDQwMTdaMD8G
# CSqGSIb3DQEJBDEyBDCy62UnHdZkaEAF28AhCbM99FE2VM5NgGJdba0To2rmFpmZ
# TSTnxIPJVQTcLeD2m80wge0GCyqGSIb3DQEJEAIMMYHdMIHaMIHXMBYEFK5ir3UK
# DL1H1kYfdWjivIznyk+UMIG8BBQC1luV4oNwwVcAlfqI+SPdk3+tjzCBozCBjqSB
# izCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcT
# C0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAs
# BgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkCEDAP
# b6zdZph0fKlGNqd4LbkwDQYJKoZIhvcNAQEBBQAEggIANZvO+YYXJ7AiO+NWqgmE
# 4inoS2zdq9+hhqsGDOVFDVIEMudEfUH0x3i79QWNzUhKG+pQAxhcYIChCk80dU4N
# je8oCiI1wbFPuEACJE1VZ0/MN1tSQctzUFVRTfao85xeD9+7zn01uLPqvf2U7rg/
# NPOC4Bu0A2VgAbE9wm2RW0/TbbT3SPS3doiLFTcewbZzTXlsmnvOZz7olrMjxxkL
# Npih2egIpy0M/GDkn0nUGCKqyV5YIQVRXjwKm2URD6x8FwsIaSUWTzDs5hQ0SKN1
# vn2+bDQNo4oobw3q6wnWO++JA/yDeb/C4FP3Xaa+iFZu/JHhesdnlzY2gvkh5Wwu
# 2J+m9ZiUISgZM4gWrjoiSV4kMuy4FEhp6xM2EGqWsOJl4A2c3ENrZQc+sKwWYtoq
# jzxibMghAvMIhC34UEKx2XgieAxrz92xak2c2fcHkF/FVNfa9fF2Le2bCKbyaC9s
# NnzgVw+IxsNI6O8B7rNvj10mMQHaPiOrD+Y0kP+2ZA5/o7zAVdfUbsEV9Wiv5OVZ
# JMl2hIT1HAr1j6H8mZDCWY9KEmzOcBNtSSD/9NputUPafSx5hdlmCMYYNHcMxw1Z
# ppJl4kz/tOXzVStVkc7frsYOC7IY8PYJKoLBRzGbFJA18m+qXxcECipQyBRCrnq+
# NQjCEnUf6+uB/kNirbizriE=
# SIG # End signature block